namespace OasisCiq.XS
{
    using System;
    using System.Xml;

    /// <summary>
    /// <para>The dateTime data type is used to specify a date and a time.</para>
    /// <para>The dateTime is specified in the following form "YYYY-MM-DDThh:mm:ss" where:</para>
    /// <list type="bullet">
    ///     <item>
    ///         <term>YYYY</term>
    ///         <description>indicates the year</description>
    ///     </item>
    ///     <item>
    ///         <term>MM</term>
    ///         <description>indicates the month</description>
    ///     </item>
    ///     <item>
    ///         <term>DD</term>
    ///         <description>indicates the day</description>
    ///     </item>
    ///     <item>
    ///         <term>T</term>
    ///         <description>indicates the start of the required time section</description>
    ///     </item>
    ///     <item>
    ///         <term>hh</term>
    ///         <description>indicates the hour</description>
    ///     </item>
    ///     <item>
    ///         <term>mm</term>
    ///         <description>indicates the minute</description>
    ///     </item>
    ///     <item>
    ///         <term>ss</term>
    ///         <description>indicates the second</description>
    ///     </item>
    /// </list>
    /// <para>To specify a time zone, you can either enter a dateTime in UTC time by adding a "Z" behind the time - like this:</para>
    /// <code>2002-05-30T09:30:10Z</code>
    /// <para>or you can specify an offset from the UTC time by adding a positive or negative time behind the time - like this:</para>
    /// <code>2002-05-30T09:30:10-06:00</code>
    /// <para>or</para>
    /// <code>2002-05-30T09:30:10+06:00</code>
    /// </summary>
    public class XSDateTime
    {
        public XSDateTime()
            : this(DateTimeOffset.UtcNow)
        {
        }

        public XSDateTime(DateTime value)
            : this(new DateTimeOffset(value))
        {
        }

        public XSDateTime(DateTime value, TimeSpan offset)
            : this(new DateTimeOffset(value, offset))
        {
        }

        public XSDateTime(DateTimeOffset value)
        {
            Value = value;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentException">
        ///     With inner <see cref="ArgumentOutOfRangeException"/> or <see cref="FormatException"/>
        /// </exception>
        public XSDateTime(string value)
        {
            if (value == null)
            {
                throw new ArgumentNullException();
            }

            try
            {
                Value = XmlConvert.ToDateTimeOffset(value);
            }
            catch (ArgumentOutOfRangeException ex)
            {
                throw new ArgumentOutOfRangeException(ex.Message, ex);
            }
            catch (FormatException ex)
            {
                throw new ArgumentException(ex.Message, ex);
            }
        }

        public DateTimeOffset Value { get; set; }

        public static implicit operator XSDateTime(DateTime argument)
        {
            return new XSDateTime(argument);
        }

        public static implicit operator XSDateTime(DateTimeOffset argument)
        {
            return new XSDateTime(argument);
        }

        public static implicit operator DateTimeOffset(XSDateTime argument)
        {
            if (argument == null)
            {
                throw new ArgumentNullException();
            }

            return argument.Value;
        }

        public static explicit operator DateTime(XSDateTime argument)
        {
            if (argument == null)
            {
                throw new ArgumentNullException();
            }
            
            return argument.Value.DateTime;
        }

        public override string ToString()
        {
            return XmlConvert.ToString(Value);
        }
    }
}